/**
 * 
 * @param {object} options
 * @param {string} options.url - 连接串
 * @param {Array<string>} options.protocols - new WebSocket的第二个参数
 */
export function Connect(options) {
    // 这个连接指的是，后端将用户信息与ws是否完成对应。
    this.isConnected = false;
    this.isConnecting = false;
    this.options = options;

    this.event = {
        open: [],
        close: [],
        message: [],
    }

    this.ws = null;
}

/**
 * @public
 * @param {Object<string, string | number>} options.param - 第一次连接时，将param对象上的键值对按照urlencode解析到url上
 */
Connect.prototype.connect = function(param) {
    if (this.isConnecting) {
        return Promise.reject('正在连接中');
    }
    if (this.isConnected) {
        return Promise.reject('已经连接');
    }
    this.isConnecting = true;
    const { url, protocols } = this.options;
    let goal = url;
    if (param) {
        goal += '?';
        Object.keys(param).forEach((key, index) => {
            goal += `${key}=${param[key]}`;
            if (index !== Object.keys(param).length - 1) {
                goal += '&';
            }
        });
    }
    this.ws = new WebSocket(goal, protocols);
    return new Promise((resolve, reject) => {
        this.ws.addEventListener('error', () => {
            this.isConnecting = false;
            reject();
        });
        let ev = () => {
            // this.ws.removeEventListener('open', ev);   
            this.isConnected = true;
            this.isConnecting = false;
            this.ws.addEventListener('close', () => {
                this.isConnected = false;
            })
            resolve();
        }
        this.ws.addEventListener('open', ev);
    })
}

/**
 * @public
 * @param {string} eventName 
 * @param {function} fun 
 */
Connect.prototype.addEventListener = function(eventName, fun) {
    if (this.event[eventName]) {
        this.event[eventName].push(fun);
        if (this.isConnected) {
            this.ws.addEventListener(eventName, fun);
        }
    }
}

/**
 * @public
 * @param {string} eventName 
 * @param {function} fun 
 */
Connect.prototype.removeEventListener = function(eventName, fun) {
    if (this.event[eventName]) {
        this.event[eventName] = this.event[eventName].filter(item => {
            if (item !== fun) {
                return true;
            }
            return false;
        })
        if (this.isConnected) {
            this.ws.removeEventListener(eventName, fun);
        }
    }
}

/**
 * @public
 */
Connect.prototype.close = function () {
    this.ws.close();
};

/**
 * @public
 * @param {string | blob | ArrayBuffer} msg 
 */
Connect.prototype.send = async function (msg) {
    if (this.isConnected) {
        this.ws.send(msg);
    } else {
        await this.connect();
        this.ws.send(msg);
    }
};

/**
 * @private
 */
Connect.prototype.bindEvent = function() {
    if (this.isConnected) {
        Object.keys(this.event).forEach(key => {
            this.event[key].forEach(ev => {
                this.ws.addEventListener(key, ev);
            })
        })
    }
}